package com.lain.modules.sys.service.impl;

import com.lain.common.utils.Constant;
import com.lain.common.vo.EntityCache;
import com.lain.config.CacheConfig;
import com.lain.modules.sys.service.CacheService;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;


/**
 * Title:       CacheServiceLocalMapImpl
 * Description: 缓存服务实现类   使用本地Map 实现单机缓存策略
 *
 * @author : liugaoyang
 * date         : 2019/3/12 11:29
 */
@Service("cacheService")
public class CacheServiceLocalMapImpl implements CacheService {

    /** 本地内存数据库 **/
    private static volatile Map<String, Map<String, Object>> localCacheDB;

    public static final boolean isOpenRedisPubSub = true;

    @Override
    public Object get(String key, int indexdb) {
        if (this.isContains(key, indexdb)) {
            // 如果没有指定数据库序号，默认用DB0
            Map<String, Object> db0 = getDbByIndex(indexdb);
            Object o = db0.get(key);
            if(o != null && o instanceof EntityCache){
                EntityCache cache = (EntityCache) o;
                try {
                    String data = (String) cache.getData();
                    return data;
                } catch (Exception e) {
                    return null;
                }
            }
        }
        return null;
    }

    @Override
    public void set(String key, Object value, int indexdb, long expire) {
        expire = expire > 0 ? expire : CacheConfig.NOT_EXPIRE;
        Map<String, Object> db = getDbByIndex(indexdb);
        db.put(key, new EntityCache(value, expire, System.currentTimeMillis()));
    }

    @Override
    public Object hget(String key, String item, int indexdb) {
        if(isContains(key, indexdb)){
            Map<String, Object> db = getDbByIndex(indexdb);
            // key值 得到一个 Object 类型的Map对象
            Object cacheObj = db.get(key);
            Object res = getValueByKeyFromObj(cacheObj, item);
            return res;
        }
        return null;
    }

    @Override
    public Map<Object, Object> hmget(String key, int indexdb) {
        Map<String, Object> db = getDbByIndex(indexdb);
        if(isContains(key, indexdb)){
            // key值 得到一个 Object 类型的Map对象
            Object cacheObj = db.get(key);
            HashMap res = getMapByKeyFromCacheObj(cacheObj);
            return res;
        } else {
            // 模仿redis，当key值不存在时，new一个新map返回，以防止空指针报错
            Map<Object, Object> valueMap = new HashMap<>();
            return valueMap;
        }
    }

    @Override
    public boolean hset(String key, String item, Object value, int indexdb) {
        Map<String, Object> db = getDbByIndex(indexdb);
        if(isContains(key, indexdb)) {
            Object cacheObj = db.get(key);
            HashMap resMap = getMapByKeyFromCacheObj(cacheObj);
            resMap.put(item, value);
        } else {
            // 把 (key -> Map) set 进指定index的DB中
            Map<String, Object> newMap = new HashMap<>();
            newMap.put(item, value);
            db.put(key, new EntityCache(newMap, CacheConfig.NOT_EXPIRE, System.currentTimeMillis()));
        }
        return true;
    }

    @Override
    public boolean hmset(String key, Map<String, Object> map, int indexdb) {
        Map<String, Object> db = getDbByIndex(indexdb);
        // 兼容加上原来的map，不会覆盖
        Object cacheObj = db.get(key);
        HashMap oldMap = getMapByKeyFromCacheObj(cacheObj);
        Map<String, Object> resMap = null;
        // 如果两个map都不为空,则做合并操作
        if(map != null && !map.isEmpty() && oldMap != null && !oldMap.isEmpty()){
            map.forEach((k, v) -> {
                oldMap.put(k, v);
            });
            resMap = oldMap;
        }
        // 如果传进来的map为空,则用oldmap
        else if(map == null || map.isEmpty()){
            resMap = oldMap;
        }
        // 如果oldMap为空,则用新Map
        else if(oldMap == null || oldMap.isEmpty()){
            resMap = map;
        }
        // 如果都为空,new 一个
        else {
            resMap = new HashMap<>();
        }
        // 用新的cacheEntity覆盖原来的key值
        db.put(key, new EntityCache(resMap, CacheConfig.NOT_EXPIRE, System.currentTimeMillis()));
        return true;
    }

    @Override
    public void hdel(String key, int indexdb) {
        if (this.isContains(key, indexdb)) {
            Map<String, Object> db = getDbByIndex(indexdb);
            db.remove(key);
        }
    }

    @Override
    public boolean hHasKey(String key, String item) {
        return false;
    }

    /**
     * 判断是否在缓存中
     * @param key
     * @param index
     * @return
     */
    @Override
    public boolean isContains(String key, int index) {
        Map<String, Object> db = getDbByIndex(index);
        return db.containsKey(key);
    }

    /**
     * 缓存是否超时失效
     * 默认DB0
     * @param key
     * @return
     */
    @Override
    public boolean isTimeOut(String key, int index) {
        if (!this.isContains(key, index)) {
            return true;
        }
        // 如果没有指定数据库序号，默认用DB0
        Map<String, Object> db = getDbByIndex(index);
        Object o = db.get(key);
        if(!(o instanceof EntityCache)){
            return true;
        }
        EntityCache cache = (EntityCache) o;
        long timeOut = cache.getTimeOut();
        long lastRefreshTime = cache.getLastRefreshTime();
        // 不是不失效缓存，而且当前时间-最后刷新时间 超过 缓存时间，判断为失效
        if (timeOut != CacheConfig.NOT_EXPIRE && System.currentTimeMillis() - lastRefreshTime >= timeOut) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 清除所有缓存
     * @param index
     */
    @Override
    public void clearAll(int index) {
        Map<String, Object> db = getDbByIndex(index);
        db.clear();
    }

    /**
     * 获取所有key
     * @param index
     * @return
     */
    @Override
    public Set<String> getAllKeys(int index) {
        Map<String, Object> db = getDbByIndex(index);
        return db.keySet();
    }

    /**
     * 对本地内存下面所有的DB进行监控
     * 清除过期缓存
     */
    private void startListen() {
        CacheService cs = this;
        final boolean flag = true;
        Map<String, Map<String, Object>> localCacheDB = getLocalCacheDB();
        new Thread(() -> {
            while (flag) {
                localCacheDB.forEach((k, v) -> {
                    for(String key : cs.getAllKeys(Integer.parseInt(k))) {
                        if (cs.isTimeOut(key, Integer.parseInt(k))) {
                            cs.hdel(key, Integer.parseInt(k));
                        }
                    }
                });
            }
        }).start();
    }

    /**
     * 根据Index得到对应的数据库
     * 返回对应对象的引用
     * @param index
     * @return
     */
    private Map<String, Object> getDbByIndex(int index) {
        // 不允许db index < 0
        if(index < Constant.Number.ZERO.getValue()){
            index = Constant.Number.ZERO.getValue();
        }
        String indexKey = String.valueOf(index);
        // 当切换db的时候，加锁处理，保证db各个事件处理的顺序
        synchronized (this) {
            if(this.getLocalCacheDB().containsKey(indexKey)){
                return this.getLocalCacheDB().get(indexKey);
            } else {
                // 如果没有对应数字的DB，则为该数字创建一个新的DB
                Map<String, Object> newDbMap = new ConcurrentHashMap<>();
                this.getLocalCacheDB().put(indexKey, newDbMap);
                return newDbMap;
            }
        }
    }

    /**
     * 得到本地内存数据库
     * @return
     */
    private Map<String, Map<String, Object>> getLocalCacheDB() {
        if(localCacheDB == null){
            // 双重锁单例
            synchronized (this) {
                if(localCacheDB == null){
                    localCacheDB = new ConcurrentHashMap<>();
                }
            }
        }
        return localCacheDB;
    }

    /**
     * Object类转换成EntityCache类
     * @param cacheObj
     * @return
     */
    private EntityCache getCacheEntityByObj(Object cacheObj) {
        if(cacheObj == null){
            cacheObj = new EntityCache(null, CacheConfig.NOT_EXPIRE, System.currentTimeMillis());
        }
        EntityCache cache = (EntityCache) cacheObj;
        return cache;
    }

    /**
     * 从Object类型的map对象中，根据key获取value
     * @param mapObj
     * @param item
     * @return
     */
    private Object getValueByKeyFromMapObj(Object mapObj, String item) {
        if(mapObj == null || item == null){
            return null;
        }
        // 强转并取值
        if(mapObj instanceof Map){
            HashMap map = (HashMap) mapObj;
            Object res = map.get(item);
            return res;
        }
        return null;
    }

    /**
     * 组合getCacheEntityByObj方法跟getValueByKeyFromMapObj方法
     * 根据cacheObj获取EntityCache，从EntityCache取出map，并根据item为key,得到map中的value
     * @param cacheObj
     * @param item
     * @return
     */
    private Object getValueByKeyFromObj(Object cacheObj, String item) {
        EntityCache cache = getCacheEntityByObj(cacheObj);
        if(cache != null){
            Object mapObj = cache.getData();
            Object res = getValueByKeyFromMapObj(mapObj, item);
            return res;
        } else {
            return null;
        }
    }

    /**
     * 从EntityCache Object中获取里面的map value
     * @param cacheObj
     * @return
     */
    private HashMap getMapByKeyFromCacheObj(Object cacheObj) {
        EntityCache cache = getCacheEntityByObj(cacheObj);
        if(cache != null){
            Object data = cache.getData();
            if(data != null && data instanceof Map){
                HashMap res = (HashMap) data;
                return res;
            }
        }
        HashMap newMap = new HashMap();
        cache.setData(newMap);
        // 如果cache为空或者cache里面的map为空，则new 一个新的引用返回
        return newMap;
    }

}
